Skip to content

Add CallFunctionOperation - #8437

Open
arettig wants to merge 18 commits into
quantumlib:mainfrom
arettig:callfunctionoperation
Open

arettig wants to merge 18 commits into
quantumlib:mainfrom
arettig:callfunctionoperation

Conversation

@arettig

@arettig arettig commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

This adds the experimental Cirq 2.0 feature CallFunctionOperation which
takes a CircuitFunction and a set of parameters to call it with. The underlying
circuit is not generated until _decompose_. This allows for multiple calls to
an underlying circuit without storing multiple copies of the circuit.

This adds the CallFunction operation which takes a CircuitFunction reference
as a parameter. The actual CircuitFunction is called upon _decompose_. This
allows reusing blocks of gates without huge memory increases. Serialization
will be added in a later commit.
This adds serialization to CallFunctionOperation. Notably, `CircuitFunction`
now implements `SerializableByKey`, ensuring that a circuitfunction will
not be serialized each time it is called.
The parameters of a decomposed CallFunctionOperation will be the
parameters of the template circuit, minus the function parameters,
plus the parameters in the invoked parameters.
@github-actions github-actions Bot added the size: L 250< lines changed <1000 label Oct 14, 2026
@codecov

codecov Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.58%. Comparing base (8a8fc5c) to head (cd18ebf).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8437      +/-   ##
==========================================
- Coverage   99.58%   99.58%   -0.01%     
==========================================
  Files        1137     1139       +2     
  Lines      105119   105594     +475     
==========================================
+ Hits       104687   105158     +471     
- Misses        432      436       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@arettig
arettig marked this pull request as ready for review October 15, 2026 20:15
@arettig
arettig requested a review from a team as a code owner October 15, 2026 20:15

@mhucka mhucka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments.

@dstrain115 dstrain115 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few edge cases from Mike and myself to look into, but overall the structure seems good to me. Excited to see this in action!

This adds the measurement_key_map and parent_path properties to
CallFunctionOperation. This unifies the protocol interface and
allows circuit functions to be called within circuitoperations
with repetitions.
@github-actions github-actions Bot added size: XL lines changed >1000 and removed size: L 250< lines changed <1000 labels Oct 27, 2026
@arettig

arettig commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

I added the measurement key related methods that were missing as pointed out by Mike (which will probably be necessary for QEC).

This led to a bit of code duplication with CircuitOperation with measurement key maps and so forth. It seems I may have just recreated a slightly more syntax friendly CircuitOperation...

@arettig
arettig requested review from dstrain115 and mhucka October 27, 2026 05:33

@mhucka mhucka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.

In addition to the notes below, Gemini had a suggestion for an architectural change:

  • CircuitFunction + CallFunctionOperation provides two capabilities that CircuitOperation does not have: (1) parameterized qubit placement via VariableQid resolution, and (2) SerializableByKey deduplication of the CircuitFunction template across calls with positional function_args.
  • However, once CircuitFunction is defined, CallFunctionOperation is effectively a parameterized wrapper that produces a FrozenCircuit. Rather than re-implementing measurement key mapping, parent_path, and _extern_keys rescoping separately in CallFunctionOperation (which is where Finding 1 above arises), CallFunctionOperation could either share the key-scoping helper functions with CircuitOperation or delegate measurement/control key scoping to an underlying CircuitOperation(self._circuit_function.circuit, ...).

It sounds plausible, but you're the best judge of whether that's a good idea or not.

def with_qubits(self, *new_qubits: cirq.Qid) -> CallFunctionOperation:
if tuple(new_qubits) == self._qubits:
return self
raise ValueError("CallFunctionOperation does not support `with_qubits`.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test case for what happens when a transformer is applied to something with a CallFunctionOperation and the result is subsequently simulated?

@arettig arettig Nov 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test for simulating a transformed CallFunctionOperation (although the currently used transformers do not use the with_qubits method tagged here).

with_qubits is enabled now using the underlying CircuitOperation and explicit tests were added. One thing to note is that qubit maps do not work with VariableQids currently, as they are currently broken in the CircuitOperation implementation. This should be fixed in a separate PR.

@arettig
arettig marked this pull request as draft October 29, 2026 20:11
@arettig

arettig commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Converting to a draft while I consider a redesign.

Rather than reimplement many methods, CallFunctionOperation now creates
a CircuitOperation from the CircuitFunction upon initialization. This
prevents excessive duplicate code.
@arettig

arettig commented Nov 11, 2026

Copy link
Copy Markdown
Contributor Author

I redesigned this as suggested, by adding a member variable CircuitOperation that is built from the given CircuitFunction upon construction. This delegates much of the complicated logic to the already implemented CircuitOperation, including parts of:

  • qubit mapping
  • control keys
  • parameter names

Several methods were implemented explicitly rather than delegating to the underlying CircuitOperation to avoid creating an copy of the circuit.

Finally, VariableQids currently do not work with qubit maps, a limitation inherited from CircuitOperation. This is because parameter resolution is not currently done before generating the new qubit map. This should be fixed within CircuitOperation in a separate PR.

@arettig
arettig marked this pull request as ready for review November 11, 2026 03:54
@arettig
arettig requested a review from mhucka November 11, 2026 03:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: XL lines changed >1000

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants